package cn.gpnusz.oliverwiki.service;

import cn.gpnusz.oliverwiki.entity.User;
import cn.gpnusz.oliverwiki.entity.UserExample;
import cn.gpnusz.oliverwiki.exception.BusinessException;
import cn.gpnusz.oliverwiki.exception.BusinessExceptionCode;
import cn.gpnusz.oliverwiki.mapper.UserMapper;
import cn.gpnusz.oliverwiki.req.UserLoginReq;
import cn.gpnusz.oliverwiki.req.UserQueryReq;
import cn.gpnusz.oliverwiki.req.UserResetPasswordReq;
import cn.gpnusz.oliverwiki.req.UserSaveReq;
import cn.gpnusz.oliverwiki.resp.UserLoginResp;
import cn.gpnusz.oliverwiki.resp.UserQueryResp;
import cn.gpnusz.oliverwiki.resp.PageResp;
import cn.gpnusz.oliverwiki.util.SnowFlake;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author h0ss
 * @description 用户信息系统业务层
 * @date 2021/8/24 - 20:57
 */
@Service
public class UserService {
    @Resource
    private UserMapper userMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private SnowFlake snowFlake;

    private static final Logger LOG = LoggerFactory.getLogger(UserService.class);

    /**
     * 按传入条件查询用户信息的业务方法
     *
     * @param userQueryReq : 查询条件参数
     * @return : cn.gpnusz.oliverwiki.resp.PageResp<cn.gpnusz.oliverwiki.resp.UserQueryResp>
     * @author h0ss
     */
    public PageResp<UserQueryResp> getAllByCondition(UserQueryReq userQueryReq) {
        UserExample userExample = new UserExample();
        UserExample.Criteria criteria = userExample.createCriteria();
        if (!ObjectUtils.isEmpty(userQueryReq.getLoginName())) {
            criteria.andLoginNameLike("%" + userQueryReq.getLoginName() + "%");
        }
        if (userQueryReq.getPage() != null && userQueryReq.getSize() != null) {
            PageHelper.startPage(userQueryReq.getPage(), userQueryReq.getSize());
        } else {
            PageHelper.startPage(1, 10);
        }
        List<User> userList = userMapper.selectByExample(userExample);
        return getUserResp(userList);
    }

    /**
     * 查询全部用户信息数据的业务方法
     *
     * @param userQueryReq : 查询（分页）参数
     * @return : cn.gpnusz.oliverwiki.resp.PageResp<cn.gpnusz.oliverwiki.resp.UserQueryResp>
     * @author h0ss
     */
    public PageResp<UserQueryResp> getAll(UserQueryReq userQueryReq) {
        if (userQueryReq.getPage() != null && userQueryReq.getSize() != null) {
            PageHelper.startPage(userQueryReq.getPage(), userQueryReq.getSize());
        } else {
            PageHelper.startPage(1, 100);
        }
        List<User> userList = userMapper.selectByExample(null);
        return getUserResp(userList);
    }

    /**
     * 新增或编辑用户信息的业务方法
     *
     * @param userSaveReq : 保存的用户信息数据
     * @author h0ss
     */
    public void save(UserSaveReq userSaveReq) {
        // 密码MD5加密处理
        byte[] passwordBytes = userSaveReq.getPassword().getBytes(StandardCharsets.UTF_8);
        userSaveReq.setPassword(DigestUtils.md5DigestAsHex(passwordBytes));
        User user = new User();
        BeanUtils.copyProperties(userSaveReq, user);
        // 判断是新增还是编辑
        if (user.getId() != null) {
            // 编辑时不保存对密码或用户名的修改
            user.setPassword(null);
            user.setLoginName(null);
            UserExample userExample = new UserExample();
            UserExample.Criteria criteria = userExample.createCriteria();
            criteria.andIdEqualTo(user.getId());
            userMapper.updateByExampleSelective(user, userExample);
        } else {
            User userByDb = selectByLoginName(userSaveReq.getLoginName());
            if (ObjectUtils.isEmpty(userByDb)) {
                user.setId(snowFlake.nextId());
                userMapper.insert(user);
            } else {
                // 用户名已存在 抛出自定义的业务异常
                throw new BusinessException(BusinessExceptionCode.USER_LOGIN_NAME_EXIST);
            }

        }
    }

    /**
     * 用户重置密码的业务方法
     *
     * @param userResetPasswordReq : 重置密码信息
     * @author h0ss
     */
    public void resetPassword(UserResetPasswordReq userResetPasswordReq) {
        // 密码MD5加密处理
        byte[] passwordBytes = userResetPasswordReq.getPassword().getBytes(StandardCharsets.UTF_8);
        userResetPasswordReq.setPassword(DigestUtils.md5DigestAsHex(passwordBytes));
        User user = new User();
        BeanUtils.copyProperties(userResetPasswordReq, user);
        UserExample userExample = new UserExample();
        UserExample.Criteria criteria = userExample.createCriteria();
        criteria.andIdEqualTo(user.getId());
        userMapper.updateByExampleSelective(user, userExample);
    }


    public UserLoginResp login(UserLoginReq userLoginReq) {
        // 密码MD5加密处理
        byte[] passwordBytes = userLoginReq.getPassword().getBytes(StandardCharsets.UTF_8);
        userLoginReq.setPassword(DigestUtils.md5DigestAsHex(passwordBytes));
        User user = new User();
        BeanUtils.copyProperties(userLoginReq, user);
        UserExample userExample = new UserExample();
        UserExample.Criteria criteria = userExample.createCriteria();
        criteria.andLoginNameEqualTo(user.getLoginName());
        List<User> usersByDb = userMapper.selectByExample(userExample);
        if (!usersByDb.isEmpty()) {
            User userByDb = usersByDb.get(0);
            if (!userByDb.getPassword().equals(user.getPassword())) {
                // 密码错误
                LOG.info("密码错误，输入密码为:{}, 数据库密码为:{}", userLoginReq.getPassword(), userByDb.getPassword());
                throw new BusinessException(BusinessExceptionCode.USER_LOGIN_ERROR);
            } else {
                // 密码正确
                UserLoginResp userLoginResp = new UserLoginResp();
                BeanUtils.copyProperties(userByDb, userLoginResp);
                // 生成token存入redis并返回给前端
                return storeToken2Redis(userLoginResp);
            }
        } else {
            // 用户名不存在
            LOG.info("输入的用户:{} 不存在", userLoginReq.getLoginName());
            throw new BusinessException(BusinessExceptionCode.USER_LOGIN_ERROR);
        }
    }

    /**
     * 生成token存入redis并返回给前端
     *
     * @param userLoginResp : 用户登录成功响应信息
     * @return : cn.gpnusz.oliverwiki.resp.UserLoginResp
     * @author h0ss
     */
    private UserLoginResp storeToken2Redis(UserLoginResp userLoginResp) {
        Long token = snowFlake.nextId();
        LOG.info("生成token:{},存入redis", token);
        // 使用stringRedisTemplate来操作redis 可以避免在其他客户端获取时的乱码 【序列化机制问题】
        stringRedisTemplate.opsForValue().set(Long.toString(token), JSON.toJSONString(userLoginResp), 3600, TimeUnit.SECONDS);
        userLoginResp.setToken(token);
        return userLoginResp;
    }

    /**
     * 退出登陆的业务方法
     *
     * @param token : 用户标识token
     * @author h0ss
     */
    public void logout(String token) {
        stringRedisTemplate.delete(token);
        LOG.info("从redis中删除token：{}", token);
    }

    /**
     * 删除用户信息的业务方法
     *
     * @param id : 要删除的用户信息id
     * @author h0ss
     */
    public void delete(Long id) {
        UserExample userExample = new UserExample();
        UserExample.Criteria criteria = userExample.createCriteria();
        criteria.andIdEqualTo(id);
        userMapper.deleteByExample(userExample);
    }

    /**
     * 按传入的用户名查找对应的用户信息
     *
     * @param loginName : 用户名
     * @return : cn.gpnusz.oliverwiki.entity.User
     * @author h0ss
     */
    public User selectByLoginName(String loginName) {
        UserExample userExample = new UserExample();
        UserExample.Criteria criteria = userExample.createCriteria();
        criteria.andLoginNameEqualTo(loginName);
        List<User> userList = userMapper.selectByExample(userExample);
        if (!userList.isEmpty()) {
            return userList.get(0);
        } else {
            return null;
        }
    }

    /**
     * 将用户信息查询结果封装成PageResp对象的业务方法
     *
     * @param userList : 用户信息查询结果
     * @return : cn.gpnusz.oliverwiki.resp.PageResp<cn.gpnusz.oliverwiki.resp.UserQueryResp>
     * @author h0ss
     */
    private PageResp<UserQueryResp> getUserResp(List<User> userList) {
        List<UserQueryResp> respList = new ArrayList<>();
        for (User user : userList) {
            UserQueryResp userQueryResp = new UserQueryResp();
            BeanUtils.copyProperties(user, userQueryResp);
            respList.add(userQueryResp);
        }
        PageInfo<User> pageInfo = new PageInfo<>(userList);
        LOG.info("总行数:{}", pageInfo.getTotal());
        LOG.info("总页数:{}", pageInfo.getPages());

        PageResp<UserQueryResp> pageResp = new PageResp<>();
        pageResp.setTotal(pageInfo.getTotal());
        pageResp.setList(respList);
        return pageResp;
    }
}
